package com.crungoo.reflect;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 模拟动态代理
 *
 * @author : fujc-dev@qq.com
 * @created : 2022/7/6 13:13
 */

public class Proxy {

	public static Object newInstacen(Object target) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
		Class clazz = target.getClass().getInterfaces()[0];
		String content = "";
		String proxyClassName = "$ProxyTest";
		String interfaceName = clazz.getSimpleName();
		String packageContent = "package com.zc58s;\n";
		String importContent = "import " + clazz.getName() + ";\n";
		String className = "public class " + proxyClassName + " implements " + interfaceName + " {\n";
		String propertyContent = "\tprivate " + interfaceName + " target;\n";
		String constructorContent = "\tpublic " + proxyClassName + "(" + interfaceName + " target){\n" +
				"\t\tthis.target =target;\n" +
				"\t}\n";

		String methodContent = "";
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			// 返回值名称
			String returnTypeName = method.getReturnType().getSimpleName();
			String methodName = method.getName();
			//被代理对象的方法
			Class args[] = method.getParameterTypes();
			String argContent = "";
			String paramsContent = "";
			int flag = 0;
			for (Class arg : args) {
				// 取到参数的类型名称
				String argTypeName = arg.getSimpleName();
				argContent += argTypeName + " p" + flag + ",";
				paramsContent += "p" + flag + ",";
				flag++;
			}
			if (argContent.length() > 0) {
				argContent = argContent.substring(0, argContent.lastIndexOf(","));
				paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(","));
			}
			methodContent += "\t public " + returnTypeName + " " + methodName + "(" + argContent + "){\n" +
					"\t\tSystem.out.println(\"log\");\n" +
					"\t\t target." + methodName + "(" + paramsContent + ");\n" +
					"\t}\n";
		}
		content += packageContent + importContent + className + propertyContent + constructorContent + methodContent + "}\n";

		File file = new File("E:\\MyProjects\\com\\zc58s\\$ProxyTest.java");
		if (!file.exists()) {
			file.createNewFile();
		}
		FileWriter fw = new FileWriter(file);
		fw.write(content);
		fw.flush();
		fw.close();

		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
		Iterable units = fileMgr.getJavaFileObjects(file);
		JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
		t.call();
		//Class.forName()
		fileMgr.close();
		URL[] urls = new URL[]{new URL("file:E:\\MyProjects\\")};
		URLClassLoader urlClassLoader = new URLClassLoader(urls);
		Class cls = urlClassLoader.loadClass("com.zc58s.$ProxyTest");
		Constructor constructor = cls.getConstructor(clazz);
		Object o = constructor.newInstance(target);
		return o;
	}
}
